#include <psp2/sysmodule.h>
#include <psp2/kernel/threadmgr.h>
#include <psp2/kernel/processmgr.h>
#include <psp2/display.h>
#include <psp2/ctrl.h>
#include <psp2/net/net.h>

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdarg.h>

#define __UDP_ECHO_IP_SERVER           	"172.16.30.249"
#define __UDP_ECHO_PORT_SERVER        	514

int fd;
static char net_mem[1*1024*1024];
static struct sockaddr_in sockaddrinRemote;

int print_init (void)
{
	sceSysmoduleLoadModule(SCE_SYSMODULE_NET);
	sceNetInit(&(SceNetInitParam){net_mem, sizeof(net_mem)});

	fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

	memset(&sockaddrinRemote, 0, sizeof(sockaddrinRemote));
	inet_aton(__UDP_ECHO_IP_SERVER, &sockaddrinRemote.sin_addr);
	sockaddrinRemote.sin_len   	= sizeof(struct sockaddr_in);
    sockaddrinRemote.sin_family	= AF_INET;
    sockaddrinRemote.sin_port	= htons(__UDP_ECHO_PORT_SERVER);

    return 0;
}

void print_deinit (void)
{
	close(fd);
	sceNetTerm();
	sceSysmoduleUnloadModule(SCE_SYSMODULE_NET);
}

int net_putc (char c)
{
	sendto(fd, (const void *)&c, 1, 0,
           (const struct sockaddr *)&sockaddrinRemote,
		   sizeof(struct sockaddr_in));
    
    return 0;
}

//printf functions
static char digits[] = "0123456789abcdef";

static void print_int(int xx, int base, int sign)
{
    char buf[16];
    int i;
    unsigned int x;

    if(sign && (sign = xx < 0))
        x = -xx;
    else
        x = xx;

    i = 0;
    do {
        buf[i++] = digits[x % base];
    } while((x /= base) != 0);

    if(sign)
        buf[i++] = '-';

    while(--i >= 0)
        net_putc(buf[i]);
}

static void print_ptr(unsigned long x)
{
    int i;
    net_putc('0');
    net_putc('x');

    for (i = 0; i < (sizeof(unsigned long) * 2); i++, x <<= 4)
        net_putc(digits[x >> (sizeof(unsigned long) * 8 - 4)]);
}

// Print to the console. only understands %d, %x, %p, %s.
void print(char *fmt, ...)
{
    va_list ap;
    int i, c;
    char *s;

    if (fmt == 0)
        return;

    va_start(ap, fmt);
    for(i = 0; (c = fmt[i] & 0xff) != 0; i++) {
        if(c != '%'){
            net_putc(c);
            continue;
        }

        c = fmt[++i] & 0xff;
        if(c == 0)
            break;

        switch(c){
        case 'd':
            print_int(va_arg(ap, int), 10, 1);
            break;

        case 'x':
            print_int(va_arg(ap, int), 16, 1);
            break;

        case 'p':
            print_ptr(va_arg(ap, unsigned long));
            break;

        case 's':
            if((s = va_arg(ap, char*)) == 0)
                s = "(null)";

            for(; *s; s++)
                net_putc(*s);
            break;

        case '%':
            net_putc('%');
            break;

        default:
            // Print unknown % sequence to draw attention.
            net_putc('%');
            net_putc(c);
            break;
        }
    }
}